package com.wy.thread_pool.impl;

import java.util.concurrent.*;

/**
 * @author 美女
 * @date 2022/05/06 15:12
 **/
//要实现的线程池类
public class MyThreadPoolExecutor implements Executor {
    //创建线程的工厂对象
    private final ThreadFactory threadFactory;
    //临时工摸鱼的时间上线
    private final long keepAliveTime;
    private final TimeUnit unit;
    //当前正式员工数量
    private int currentCoreSize;
    //当前临时员工数量
    private int currentTemporarySize;
    //正式员工数量上限
    private final int corePoolSize;
    //临时工数量上限
    private final int temporaryPoolSize;
    //传递任务的阻塞队列
    private final BlockingQueue<Runnable> workQueue;
    //构造方法
    public MyThreadPoolExecutor(int corePoolSize,
                                int maximumPoolSize,//正式+临时
                                long keepAliveTime,
                                TimeUnit unit,
                                BlockingQueue<Runnable> workQueue,
                                ThreadFactory threadFactory,
                                RejectedExecutionHandler handler){
        this.corePoolSize=corePoolSize;
        this.temporaryPoolSize=maximumPoolSize-corePoolSize;
        this.workQueue=workQueue;
        this.threadFactory=threadFactory;
        this.keepAliveTime=keepAliveTime;
        this.unit=unit;
    }

    //向线程池中提交任务
    @Override
    public void execute(Runnable command) {
        //1.如果正式员工数量还低于正式员工数量上限，优先创建正式员工处理任务
        //1.1需要管理当前正式员工数量，正式员工数量上限——定义成属性去管理
        if(currentCoreSize<corePoolSize){
            //优先创建正式员工进行处理
            //即创建一个线程，这个线程中的任务就是不断的取任务-做任务，并且无需考虑退出问题（这里能知道需要一个线程类）
            //任务：
            CoreJob job=new CoreJob(workQueue,command);
//            //不使用工厂创建的线程
//            Thread thread=new Thread(job);//把任务job传入
            //使用工厂创建的线程
            Thread thread=threadFactory.newThread(job);
            String name=String.format("正式员工-%d",currentCoreSize);
            thread.setName(name);
            thread.start();//启动线程

            //CoreJob job=new CoreJob(workQueue);时
            //只创建了线程，此刻提交的command任务还未执行——有两种不同的策略：
            //策略1.把command放入队列【command的执行次序是在队列已有的任务之后】
            //策略2.创建正式员工时就把command提交给正式员工【让command优先执行】
            //我们采用第二种（主要是Java官方用的第二种，但这两种没有好坏之分）
            //即提交当前队列时把任务也同时提交给它： CoreJob job=new CoreJob(workQueue，command);


            currentCoreSize++;
            return;
        }
        //走到这正式员工数量==上限
        //2.优先把任务放入队列，如果放入成功，execute执行结束，否则继续放
        //2.1需要一个阻塞队列
//        //观察下一行放入
//        workQueue.put(command);//带阻塞的放入，是否满足这里的需求
//                               //我们这里希望立即得到结果，所以不能使用带阻塞的放入
//                               //使用传统队列的放法
        boolean success = workQueue.offer(command);
        if(success==true){
            //说明放入队列成功
            return;
        }
        //否则走到这说明队列已经放满了
        //3.继续判断，临时工数量是否上限，如果没有，创建临时工处理
        if(currentTemporarySize<temporaryPoolSize){
            //创建临时工进行处理
            TemporaryJob job=new TemporaryJob(keepAliveTime,unit,workQueue,command);
            //使用工厂创建的线程
            Thread thread=threadFactory.newThread(job);
            String name=String.format("临时员工-%d",currentTemporarySize);
            thread.setName(name);
            thread.start();//启动线程


            currentTemporarySize++;
            return;
        }
        //走到这里，再不能放入了
        //4.执行拒绝策略
        //为了实现方便，暂时不考虑其他策略，直接抛出异常
        throw new RejectedExecutionException();
    }
}
